Debuggers are software tools that can be used to diagnose computer programs and trace errors that arise during execution of the program. Debuggers are commonly used during the development of computer programs, to verify that a program under development operates as intended.
To support a debugger, information describing symbols and types in the program as well as information to map between source (human readable) and executable (machine readable) code is typically required. In the case of compiled programs, a compiler can often produce this information. This extra information, generally referred to as debugging information, enables a programmer to examine the types, variables and data structures used in a program by name and to follow the execution of the program through the source code.
A number of debugging techniques can be used to enable a programmer to properly analyze a program to detect points in the program where errors occur. One such technique is to put a breakpoint into the program, at a point in the program where it is desirable for normal operation to be suspended automatically when certain conditions are met. Breakpoints are useful for program testing. They are specified by programmers so that interim results of processing can be inspected, and then programs can be restarted to continue running normally.
In traditional debuggers, breakpoints are generally set on specific statements within functions or methods; program execution is suspended each time the particular statement is called. In some environments, breakpoints may also be specified to be “conditional” so that execution is suspended by a breakpoint only when a particular condition associated with that breakpoint is met (e.g., after the breakpoint has been hit X times).
Statement-based breakpoints have been found to be very useful for debugging program bugs and inefficiencies in traditional procedural languages that are sequential in nature. However, object-oriented languages, which have found increasing acceptability as programming languages, are based on quite a different concept.
Object-oriented programs are typically created using a set of language tools, abstractions and constructs that support a particular form of user-defined type called a “class”. A class functions as a template, and associates a collection of declared data with a set of operations capable of being performed on that data, i.e., methods capable of being called on the data. In an object-oriented program, objects are dynamically created during runtime using the classes as templates. The creation of a new object is often referred to as “instantiation,” whereby an object is an “instance” of a class.
A fundamental feature of object-oriented programming is that classes can be related to one another by inheritance. The properties, behaviors, data and operations of a parent, or “base” class may be inherited without modification by some child, or “derived” class, or the behavior, properties and operations may be selectively refined under the control of the programmer in the derived class. The latter function is generally referred to as overriding. When defining a derived class, one may start by building on an existing base class that is similar to the one to be created. The derived class inherits the implementation and behavior of the base class, including its methods and data, except as modified by overriding amendments detailed in the derived class definition. Several classes can inherit the behaviors of a common parent, and in some object-oriented languages a derived class may inherit from more than one base class.
In a working program, objects are instantiated, or created as needed, and built from the templates defined by their respective classes. During runtime, it is often desirable to provide each new object with initial data and/or initiate particular operations with the object. For this reason, many object-oriented environments support special methods known as constructors, or creators, that are called upon an object's creation. One or more constructor methods are typically defined in each class, while a default constructor method may be defined for some classes when no explicit method is defined by the developer.
Many classes support multiple constructor methods that perform different operations based upon how many and what types of data values are provided when creation of an object is desired. Constructor methods typically specify particular call signatures that define the types of data values expected by those methods, so that, when object creation is initiated, the data values supplied therewith can be analyzed to locate a matching constructor method, and the matching constructor method can then be executed.
The use of multiple constructor methods for a class provides significant flexibility for programmers. However, the flexibility in object creation has shortcomings during debugging of the computer program. For example, a programmer may desire to track when objects of a particular class have been created. Conventional breakpoints, which are statement based, could be set on statements in individual constructor methods so that the programmer is notified whenever a statement in a constructor method is hit during creation of an object. However, since more than one constructor may be responsible for creating these objects, a programmer is typically required to manually set a breakpoint on a statement in each constructor method for a class. Manually setting a breakpoint on a statement in each constructor method, however, can be excessively burdensome and time consuming, and a distinct risk exists that a programmer may inadvertently forget to set a breakpoint in one of the constructor methods, or forget to remove all of the breakpoints once the condition has been adequately tested.
In addition, in some situations a programmer may wish to track the number of objects that are created for a particular class. As an example, a programmer may wish to verify that an excessive number of objects are not created for a particular purpose, e.g., in a database environment, where a programmer might intend for no more than 10 database connections to be active at any given time. Through the use of manually-set breakpoints in all constructor methods, a programmer could manually count the number of object creations; however, doing so could be unduly burdensome when tens, hundreds or thousands of objects are normally created in a program.
Should halting execution after each object creation be unduly burdensome, a programmer could utilize conditional breakpoints in each constructor method to trigger only after a certain number of hits. However, given that each breakpoint would independently track the number of times it was hit, and given that a programmer may not know the relative frequency that each constructor method for a particular object is called, the programmer would still not be able to be notified after a specific number of objects were created.
Consequently, a significant need exists for a method of debugging an object-oriented computer program that facilitates the tracking of object creation operations during execution of the computer program.